package com.linkoog.devtools.code_generator.database.sqlparser.parser;

import com.linkoog.devtools.code_generator.database.domain.ColumnInfo;
import com.linkoog.devtools.code_generator.database.domain.TableInfo;
import com.linkoog.devtools.code_generator.database.sqlparser.ParseResult;
import com.linkoog.devtools.code_generator.database.sqlparser.ParseState;
import com.linkoog.devtools.code_generator.util.StringUtil;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import org.jetbrains.annotations.NotNull;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractSqlParser implements SqlParser{

    public ParseResult parseCreateTable(String ddl){
        ParseResult result = new ParseResult();
        if (StringUtil.isBlank(ddl)){
            return result.setParseState(ParseState.SQL_BLANK);
        }

        List<TableInfo> tableInfoList = new ArrayList<>();
        Map<String,String> commentMap = new HashMap<>();
        try {
            String[] ddlSqls = ddl.split(";");
            for (String ddlSql : ddlSqls) {
                if (StringUtil.isBlank(ddlSql)){
                    continue;
                }

                ddlSql = ddlSql.replace("\ufeff","");

                if (isCreateTableSql(ddlSql)){
                    Statement sqlStmt = CCJSqlParserUtil.parse(new StringReader(ddlSql));
                    if (!(sqlStmt instanceof CreateTable)) {
                        //return result.setParseState(ParseState.NOT_CREATE_TABLE_DDL);
                        continue;
                    }

                    CreateTable createTable = (CreateTable)sqlStmt;

                    TableInfo tableInfo = new TableInfo();
                    parseTableInfo(createTable, tableInfo);

                    List<ColumnInfo> columnInfoList = new ArrayList<>();
                    parseColumnInfo(createTable, columnInfoList);
                    tableInfo.setColumnInfos(columnInfoList);
                    tableInfo.setChecked(true);

                    tableInfoList.add(tableInfo);
                }


                if (isCommentSql(ddlSql)){
                    List<String> items = getNotBlankStrings(ddlSql);
                    if (items.size() > 5){
                        String item2 = items.get(2);
                        String item3 = items.get(3).replaceAll("\"","");
                        String item5 = items.get(5).replaceAll("'","");

                        if ("TABLE".equalsIgnoreCase(item2)){
                            String[] temps = item3.split("\\.");
                            commentMap.put(temps[temps.length-1] , item5);
                        } else if ("COLUMN".equalsIgnoreCase(item2)){
                            String[] temps = item3.split("\\.");
                            if (temps.length >= 2){
                                commentMap.put(temps[temps.length-2] + "." + temps[temps.length-1], item5);
                            }
                        }
                    }
                }
            }

            for (TableInfo tableInfo : tableInfoList) {
                String comment = commentMap.get(tableInfo.getTableName());
                if(StringUtil.isNotBlank(comment)){
                    tableInfo.setTableComment(comment);
                }

                for (ColumnInfo columnInfo : tableInfo.getColumnInfos()) {
                    comment = commentMap.get(tableInfo.getTableName() + "." + columnInfo.getColumnName());
                    if(StringUtil.isNotBlank(comment)){
                        columnInfo.setColumnComment(comment);
                    }
                }
            }

            result.setTableInfos(tableInfoList);
            return result;

        } catch (JSQLParserException e) {
            result.setParseState(ParseState.PARSE_FAIL);
            result.setErrorMessage(e.getMessage());
            return result;
        }

        //result.setTableInfo(tableInfo);
    }


    private boolean isCreateTableSql(String ddlSql){
        if (StringUtil.isBlank(ddlSql)){
            return false;
        }

        List<String> results = getNotBlankStrings(ddlSql);
        if (results.size() > 2){
            String item0 = results.get(0);
            String item1 = results.get(1);

            if ("CREATE".equalsIgnoreCase(item0) && "TABLE".equalsIgnoreCase(item1)){
                return true;
            }
        }
        return false;
    }

    @NotNull
    private List<String> getNotBlankStrings(String ddlSql) {
        List<String> results = new ArrayList<>();
        String[] items = ddlSql.split("\\s+");
        for (String item : items) {
            if (StringUtil.isNotBlank(item)){
                results.add(item);
            }
        }
        return results;
    }

    private boolean isCommentSql(String ddlSql){
        if (StringUtil.isBlank(ddlSql)){
            return false;
        }

        List<String> results = getNotBlankStrings(ddlSql);
        if (results.size() > 5){
            String item0 = results.get(0);
            String item1 = results.get(1);
            String item2 = results.get(2);

            if ("COMMENT".equalsIgnoreCase(item0) && "ON".equalsIgnoreCase(item1)
                    && ("TABLE".equalsIgnoreCase(item2) || "COLUMN".equalsIgnoreCase(item2))){
                return true;
            }
        }

        return false;
    }

    protected abstract void parseTableInfo(CreateTable createTable, TableInfo tableInfo);

    protected void parseColumnInfo(CreateTable createTable, List<ColumnInfo> columnInfoList){
        List<ColumnDefinition> columnDefinitionList = createTable.getColumnDefinitions();
        for (ColumnDefinition columnDefinition : columnDefinitionList) {
            ColumnInfo columnInfo = new ColumnInfo();
            parseColumnInfo(columnDefinition,columnInfo);

            columnInfoList.add(columnInfo);
        }
    }

    protected abstract void parseColumnInfo(ColumnDefinition columnDefinition, ColumnInfo columnInfo);




}
